package com.aaron.remoting.transport.netty.codec;

import com.aaron.compress.Compress;
import com.aaron.enums.CompressTypeEnum;
import com.aaron.enums.SerializationTypeEnum;
import com.aaron.extension.ExtensionLoader;
import com.aaron.remoting.constants.RpcConstants;
import com.aaron.remoting.dto.RpcMessage;
import com.aaron.remoting.dto.RpcRequest;
import com.aaron.remoting.dto.RpcResponse;
import com.aaron.serialize.Serializer;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import lombok.extern.slf4j.Slf4j;

import java.util.Arrays;

/**
 * custom protocol decoder
 * <pre>
 *   0     1     2     3     4        5     6     7     8    9          10      11       12  13  14   15    16
 *   +-----+-----+-----+-----+--------+----+----+----+------+-----------+-------+----- --+-----+-----+-------+
 *   |   magic   code        |version | full length         | messageType| codec|compress|    RequestId       |
 *   +-----------------------+--------+---------------------+-----------+-----------+-----------+------------+
 *   |                                                                                                       |
 *   |                                         body                                                          |
 *   |                                                                                                       |
 *   |                                        ... ...                                                        |
 *   +-------------------------------------------------------------------------------------------------------+
 * 4B  magic code（魔法数）   1B version（版本）   4B full length（消息长度）    1B messageType（消息类型）
 * 1B compress（压缩类型） 1B codec（序列化类型）    4B  requestId（请求的Id）
 * body（object类型数据）
 * </pre>
 *
 * <p>
 * LengthFieldBasedFrameDecoder是一种基于长度的解码器，用于解决TCP解包和粘贴问题。
 * LengthFieldBasedFrameDecoder类：继承自ByteToMessageDecoder编码器，入站
 * </p>
 * 解码
 * 1.这个类的作用就是解析 上下文（ChannelHandlerContext）数据帧中的字节
 */
@Slf4j
public class RpcMessageDecoder extends LengthFieldBasedFrameDecoder {
    public RpcMessageDecoder() {
        // lengthFieldOffset字段偏移长度: magic code is 4B, and version is 1B, and then full length. so value is 5
        // lengthFieldLength长度字段的长度: full length is 4B. so value is 4
        // lengthAdjustment长度的调整值: 全长包括所有数据并读取之前的9个字节，所以左边的长度是（fullLength-9）。所以值为 -9
        // initialBytesToStrip，初始字节需要跳过: we will check magic code and version manually, so do not strip any bytes. so values is 0。手动检查，所以不需要删除任何字节
        this(RpcConstants.MAX_FRAME_LENGTH, 5, 4, -9, 0);
    }

    /**
     * @param maxFrameLength      最大帧长度。它决定了可以接收的最大数据长度。如果超过，数据将被丢弃。
     * @param lengthFieldOffset   长度字段偏移量。长度字段是跳过指定长度字节的字段。
     * @param lengthFieldLength   表示该帧大小的字段full length, 所占有的字节数
     * @param lengthAdjustment    要添加到长度字段值 的 补偿值或者叫调整值
     * @param initialBytesToStrip Number of bytes skipped. 跳过的字节数。
     *                            If you need to receive all of the header+body data, this value is 0。 如果需要接收所有的header+body数据，这个值为0。
     *                            if you only want to receive the body data, then you need to skip the number of bytes consumed by the header.如果只想接收body数据，则需要跳过header消耗的字节数。
     */
    public RpcMessageDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength,
                             int lengthAdjustment, int initialBytesToStrip) {
        super(maxFrameLength, lengthFieldOffset, lengthFieldLength, lengthAdjustment, initialBytesToStrip);
    }

    // 入栈，对帧进行解码
    @Override
    protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
        Object decoded = super.decode(ctx, in);
        if (decoded instanceof ByteBuf) {
            ByteBuf frame = (ByteBuf) decoded;
            // 帧的头部的长度就是16，所以大于等于16该帧才有意义
            if (frame.readableBytes() >= RpcConstants.TOTAL_LENGTH) {
                try {
                    // 调用帧解码的方法
                    return decodeFrame(frame);
                } catch (Exception e) {
                    log.error("Decode frame error!", e);
                    throw e;
                } finally {
                    frame.release();
                }
            }

        }
        return decoded;
    }


    // 对帧进行解码，读取里面的信息去填充RpcMessage对象，并检查
    private Object decodeFrame(ByteBuf in) {
        // 检查魔数与版本号
        checkMagicNumber(in);
        checkVersion(in);
        // 读取4个字节的数
        int fullLength = in.readInt();
        // 把读取的消息 填充到RpcMessage对象中
        byte messageType = in.readByte();
        byte codecType = in.readByte();
        byte compressType = in.readByte();
        int requestId = in.readInt(); //这也是4个字节
        RpcMessage rpcMessage = RpcMessage.builder()
                .codec(codecType)
                .requestId(requestId)
                .messageType(messageType).build();
        // 如果该帧是测试心跳连接的，那么填充ping,pong
        if (messageType == RpcConstants.HEARTBEAT_REQUEST_TYPE) {
            rpcMessage.setData(RpcConstants.PING);
            return rpcMessage;
        }
        if (messageType == RpcConstants.HEARTBEAT_RESPONSE_TYPE) {
            rpcMessage.setData(RpcConstants.PONG);
            return rpcMessage;
        }
        // body对象的大小就是 fullLength-头部的16字节
        int bodyLength = fullLength - RpcConstants.HEAD_LENGTH;
        if (bodyLength > 0) {
            byte[] bs = new byte[bodyLength];
            in.readBytes(bs); // 读取body所有字节
            // 解压body所有字节
            String compressName = CompressTypeEnum.getName(compressType);
            Compress compress = ExtensionLoader.getExtensionLoader(Compress.class)
                    .getExtension(compressName);
            bs = compress.decompress(bs);
            // 反序列化出对象
            String codecName = SerializationTypeEnum.getName(rpcMessage.getCodec());
            log.info("codec name: [{}] ", codecName);
            Serializer serializer = ExtensionLoader.getExtensionLoader(Serializer.class)
                    .getExtension(codecName);
            if (messageType == RpcConstants.REQUEST_TYPE) { // 是request对象
                RpcRequest tmpValue = serializer.deserialize(bs, RpcRequest.class);
                rpcMessage.setData(tmpValue);
            } else { // 是response对象
                RpcResponse tmpValue = serializer.deserialize(bs, RpcResponse.class);
                rpcMessage.setData(tmpValue);
            }
        }
        return rpcMessage;

    }

    // 版本号必须是1
    private void checkVersion(ByteBuf in) {
        // read the version and compare
        byte version = in.readByte();
        if (version != RpcConstants.VERSION) {
            throw new RuntimeException("version isn't compatible" + version);
        }
    }

    // 魔数，4个字节，必须是g r p c
    private void checkMagicNumber(ByteBuf in) {
        // read the first 4 bit, which is the magic number, and compare
        int len = RpcConstants.MAGIC_NUMBER.length;
        byte[] tmp = new byte[len];
        in.readBytes(tmp);
        for (int i = 0; i < len; i++) {
            if (tmp[i] != RpcConstants.MAGIC_NUMBER[i]) {
                throw new IllegalArgumentException("Unknown magic code: " + Arrays.toString(tmp));
            }
        }
    }

}
